#!/usr/bin/python3
"""
Pre-populate /var directory for a given stateroot.
"""


import contextlib
import os
import sys
import subprocess

import osbuild.api
from osbuild.util import ostree


SCHEMA = """
"additionalProperties": false,
"required": ["deployment"],
"properties": {
  "deployment": {
    "additionalProperties": false,
    "required": ["osname", "ref"],
    "properties": {
      "osname": {
        "description": "Name of the stateroot to be used in the deployment",
        "type": "string"
      },
      "ref": {
        "description": "OStree ref to create and use for deployment",
        "type": "string"
      },
      "serial": {
        "description": "The deployment serial (usually '0')",
        "type": "number",
        "default": 0
      }
    }
  }
}
"""


class MountGuard(contextlib.AbstractContextManager):
    def __init__(self):
        self.mounts = []

    def mount(self, source, target, bind=True, ro=False, mode="0755"):
        options = []
        if bind:
            options += ["bind"]
        if ro:
            options += ["ro"]
        if mode:
            options += [mode]

        args = ["--make-private"]
        if options:
            args += ["-o", ",".join(options)]

        subprocess.run(["mount"] + args + [source, target], check=True)
        self.mounts += [{"source": source, "target": target}]

        subprocess.run(["mount"] + args + [source, target], check=True)

    def unmount(self):

        while self.mounts:
            mount = self.mounts.pop()  # FILO: get the last mount
            target = mount["target"]
            subprocess.run(["umount", "--lazy", target],
                           check=True)

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.unmount()


def populate_var(sysroot):
    # Like anaconda[1] and Fedora CoreOS dracut[2]
    # [1] pyanaconda/payload/rpmostreepayload.py
    # [2] ignition-ostree-populate-var.sh

    for target in ('lib', 'log'):
        os.makedirs(f"{sysroot}/var/{target}", exist_ok=True)

    for target in ('home', 'roothome', 'lib/rpm', 'opt', 'srv',
                   'usrlocal', 'mnt', 'media', 'spool', 'spool/mail'):

        if os.path.exists(f"{sysroot}/var/{target}"):
            continue

        res = subprocess.run(["systemd-tmpfiles", "--create", "--boot",
                              "--root=" + sysroot,
                              "--prefix=/var/" + target],
                             encoding="utf-8",
                             stdout=sys.stderr,
                             check=False)

        # According to systemd-tmpfiles(8), the return values are:
        #  0 → success
        # 65 → so some lines had to be ignored, but no other errors
        # 73 → configuration ok, but could not be created
        #  1 → other error
        if res.returncode not in [0, 65]:
            raise RuntimeError(f"Failed to provision /var/{target}")


def main(tree, options):
    dep = options["deployment"]
    osname = dep["osname"]
    ref = dep["ref"]
    serial = dep.get("serial", 0)

    deployment = ostree.deployment_path(tree, osname, ref, serial)
    var = os.path.join(tree, "ostree", "deploy", osname, "var")

    with MountGuard() as mgr:
        mgr.mount(var, f"{deployment}/var")
        populate_var(deployment)


if __name__ == '__main__':
    stage_args = osbuild.api.arguments()
    r = main(stage_args["tree"],
             stage_args["options"])
    sys.exit(r)
